#include "drawing.hpp"
#include <string.h>
#include "SEGGER_RTT.h"
#include <stdlib.h>
#include <stdio.h>

#define DRAWING_STR_LTH_MAX         32
using namespace device;
using namespace ui;
///>已经了解了LCD 的底层工作原理
char DrawingCharBuf[DRAWING_STR_LTH_MAX];
///> 编码解析
int16_t drawing::dEncoderFind(uint16_t usEncoder)
{
	uint16_t i;
	volatile uint16_t Buf;

	for(i = 0; i < mFontModule->usGb2312Length; i++)
	{
		Buf = mFontModule->usGb2312MapAddr[i];
		if(mFontModule->usGb2312MapAddr[i] == usEncoder)
		{
			return(i);
		}
	}
	return(-1);
}


int drawing::font_swprintf(wchar_t pwchar_out[], const char* pwchar_in, ...)
{
	int ret;
	char i;
	va_list ParamList;

	va_start(ParamList, pwchar_in);
	ret = std::vsprintf(DrawingCharBuf, pwchar_in, ParamList);
	va_end(ParamList);
	if(ret > DRAWING_STR_LTH_MAX || ret < 0)
	{
		return -1;
	}
	for(i = 0; i < ret; i++)
	{
		pwchar_out[i] = DrawingCharBuf[i];
	}
	return ret;
}
///>计算字符串占用的宽度
uint16_t drawing::MeasureString(const wchar_t *String)
{
	volatile uint16_t i, ret = 0;
	uint16_t stringlen = font_wcslen(String);
	volatile uint16_t show;

	for(i = 0; i < stringlen; i++)
	{
		show = String[i];
		if(show < mFontModule->usAscLength + 0x20)
		{
			ret += mFontModule->usAscWidth;
		}
		else
		{
			ret += mFontModule->usGb2312Width;
		}
	}
	return(ret);
}

drawing::drawing(lcd *tLcd)
{
	mLcd = tLcd;
	mFontModule = 0;
}

void drawing::InitFontModule(uint32_t uiAddr)
{
	mFontModule = (FontModuleTPDF*)uiAddr;
}

void drawing::Clear(void)
{
	mLcd->Clear();
}

void drawing::DrawLine(Paint *tPaint, uint32_t uiStartX, uint32_t uiStartY, uint32_t uiEndX, uint32_t uiEndY)
{
	int i, j;
	int rat = 0x80000000;
	uint32_t width = 1;
	uint8_t dots[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
	lcd::RGBTPDF color = {0xffff, 0xffff, 0xffff};

	if(!mLcd || !mFontModule)
	{
		return;
	}
	if(uiStartX >= mLcd->GetLcdAttribute()->Resolution.X ||
		 uiEndX >= mLcd->GetLcdAttribute()->Resolution.X ||
		 uiStartY >= mLcd->GetLcdAttribute()->Resolution.Y ||
		 uiEndY >= mLcd->GetLcdAttribute()->Resolution.Y)
	{
		return;
	}
	if(tPaint)
	{
		if(mLcd->GetLcdAttribute()->ColorType == lcd::COLOR_TYPE_COLORFULL)
		{
			memcpy(&color, &tPaint->Color, sizeof(lcd::RGBTPDF));
			if(tPaint->Invert)
			{
				color.Red = ~color.Red;
				color.Green = ~color.Green;
				color.Blue = ~color.Blue;
			}
		}
		width = tPaint->Width;
		if(tPaint->Invert)
		{
			memset(dots, 0, sizeof(dots));
		}
	}
	if(uiStartX != uiEndX)
	{
		if(uiEndY == uiStartY)
		{
			if(uiEndX < uiStartX)
			{
				i = uiStartX;
				uiStartX = uiEndX;
				uiEndX = i;
			}
			for(i = 0 ; i < width; i++)
			{

				j = uiStartY - (width >> 1) + i ; 
				if ( (j >=0) && (j <= mLcd->GetLcdAttribute()->Resolution.Y))
				{	
					if(mLcd->GetLcdAttribute()->ColorType == lcd::COLOR_TYPE_COLORFULL)
					{
						mLcd->DrawHorizontal(uiStartX, j, dots, &color, uiEndX - uiStartX + 1);
					}
					else
					{
						mLcd->DrawHorizontal(uiStartX, j, dots, uiEndX - uiStartX + 1);
					}
				}
			}
		}
		else
		{
			rat = ((((int)uiEndY) - ((int)uiStartY)) * 1024) / (((int)uiEndX) - ((int)uiStartX));
			if(uiEndX > uiStartX)
			{
				for(i = 0; i <= uiEndX - uiStartX; i++)
				{
					j = ((i * rat) >> 10) + uiStartY;
					if(mLcd->GetLcdAttribute()->ColorType == lcd::COLOR_TYPE_COLORFULL)
					{
						mLcd->DrawHorizontal(uiStartX + i, j, dots, &color, 1);
					}
					else
					{
						mLcd->DrawHorizontal(uiStartX + i, j, dots, 1);
					}
				}
			}
			else
			{
				for(i = 0; i >= uiStartX - uiEndX; i--)
				{
					j = ((i * rat) >> 10) + uiStartY;
					if(mLcd->GetLcdAttribute()->ColorType == lcd::COLOR_TYPE_COLORFULL)
					{
						mLcd->DrawVertical(uiStartX + i, j, dots, &color, 1);
					}
					else
					{
						mLcd->DrawVertical(uiStartX + i, j, dots, 1);
					}
				}
			}
		}
	}
	else
	{
		if(uiEndY < uiStartY)
		{
			i = uiStartY;
			uiStartY = uiEndY;
			uiEndY = i;
		}
		for(i = 0 ; i < width; i++)
		{
			j = uiStartX - (width >> 1) + i;
			if(j>=0 && j<= mLcd->GetLcdAttribute()->Resolution.X )
			{	
				if(mLcd->GetLcdAttribute()->ColorType == lcd::COLOR_TYPE_COLORFULL)
				{
					mLcd->DrawVertical(j, uiStartY, dots, &color, uiEndY - uiStartY + 1);
				}
				else
				{
					mLcd->DrawVertical(j, uiStartY, dots, uiEndY - uiStartY + 1);
				}
			}
		}
	}
}

void drawing::DrawRectangle(Paint *tPaint, uint32_t uiStartX, uint32_t uiStartY, uint32_t uiEndX, uint32_t uiEndY)
{
	bool paintfull = false;
	Paint P1;
	uint32_t i;

	if(!mLcd || !mFontModule)
	{
		return;
	}
	if(uiStartX >= mLcd->GetLcdAttribute()->Resolution.X ||
		 uiEndX >= mLcd->GetLcdAttribute()->Resolution.X ||
		 uiStartY >= mLcd->GetLcdAttribute()->Resolution.Y ||
		 uiEndY >= mLcd->GetLcdAttribute()->Resolution.Y)
	{
		return;
	}
	memcpy(&P1, tPaint, sizeof(Paint));
	if(uiEndX < uiStartX)
	{
		i = uiStartX;
		uiStartX = uiEndX;
		uiEndX = i;
	}
	if(uiEndY < uiStartY)
	{
		i = uiStartY;
		uiStartY = uiEndY;
		uiEndY = i;
	}
	if(tPaint)
	{
		paintfull = tPaint->PaintStyle == drawing::PAINT_STYLE_FILL ? true : false;
	}
	if(paintfull)
	{
		P1.Width = 1;
		for(i = uiStartX; i <= uiEndX; i++)
		{
			DrawLine(&P1, i, uiStartY, i, uiEndY);
		}
	}
	else
	{
		DrawLine(&P1, uiStartX, uiStartY, uiEndX, uiStartY);
		DrawLine(&P1, uiEndX, uiStartY, uiEndX, uiEndY);
		DrawLine(&P1, uiStartX, uiEndY, uiEndX, uiEndY);
		DrawLine(&P1, uiStartX, uiStartY, uiStartX, uiEndY);
	}
}
//> 计算宽字符长度
short drawing::font_wcslen(const wchar_t* pwStr)
{
	short i;

	if(pwStr == NULL)
	{
		return 0;
	}
	for(i = 0; i < 255; i++)
	{
		if(pwStr[i] == L'\0')
		{
			return i;
		}
	}
	return 0;
}

void drawing::DrawString(Paint *tPaint, const wchar_t *String, uint32_t uiX, uint32_t uiY)
{
	uint32_t i, k;
	uint8_t dots[2];
	bool ivt = false;
	bool chinese = false;
	uint16_t offsetx = uiX;
	uint8_t heightlen;
	uint8_t ucdotslen;
	int16_t show;
	lcd::RGBTPDF color = {0xffff, 0xffff, 0xffff};
	short stringlen;

	if(!mLcd || !mFontModule)
	{
		return;
	}
	if(uiX >= mLcd->GetLcdAttribute()->Resolution.X ||
		 uiY >= mLcd->GetLcdAttribute()->Resolution.Y)
	{
		return;
	}
	if(tPaint)
	{
		ivt = tPaint->Invert;
		if(mLcd->GetLcdAttribute()->ColorType == lcd::COLOR_TYPE_COLORFULL)
		{
			memcpy(&color, &tPaint->Color, sizeof(lcd::RGBTPDF));
			if(ivt)
			{
				color.Red = ~color.Red;
				color.Green = ~color.Green;
				color.Blue = ~color.Blue;
			}
		}
	}
	stringlen = font_wcslen(String);
	for(i = 0; i < stringlen; i++)
	{
		if(offsetx >= mLcd->GetLcdAttribute()->Resolution.X)
		{
			break;
		}
		show = dEncoderFind(String[i]);
		if(show == -1) 
		{
			show = String[i];
			chinese = false;
		}
		else
		{
			chinese = true;
		}
		if(chinese == false)
		{
			if(show >= ' ' && show <= '~')
			{
				show -= ' ';
			}
			else
			{
				show = ' ';
			}
		}
		for(k = 0; k < (chinese ? mFontModule->usGb2312Width : mFontModule->usAscWidth); k++)///> 写一个字节
		{
			if(chinese)
			{
				heightlen = mFontModule->usGb2312Height >> 3;
				ucdotslen = mFontModule->usGb2312Height; 
				if(mFontModule->usGb2312Height & 7)
				{
					heightlen++;
				}
				dots[0] = *(uint8_t *)(mFontModule->ucGb2312DatabaseAddr + mFontModule->usGb2312Width * heightlen * show + k * 2);
				dots[1] = *(uint8_t *)(mFontModule->ucGb2312DatabaseAddr + mFontModule->usGb2312Width * heightlen * show + k * 2 + 1);
			}
			else
			{
				heightlen = mFontModule->usAscHeight >> 3;
				ucdotslen = mFontModule->usAscHeight; 
				if(mFontModule->usAscHeight & 7)
				{
					heightlen++;
				}
				dots[0] = *(uint8_t *)(mFontModule->ucAscDatabaseAddr + mFontModule->usAscWidth * heightlen * show + k * 2);
				dots[1] = *(uint8_t *)(mFontModule->ucAscDatabaseAddr + mFontModule->usAscWidth * heightlen * show + k * 2 + 1);
			}
			if(ivt)
			{
				dots[0] = ~dots[0];
				dots[1] = ~dots[1];
			}
			mLcd->DrawVertical(offsetx + k, uiY, dots, ucdotslen);
		}
		if(chinese)
		{
			offsetx += mFontModule->usGb2312Width;
		}
		else
		{
			offsetx += mFontModule->usAscWidth;
		}
	}
}

void drawing::DrawString(Paint *tPaint, const wchar_t *String, uint32_t uiSatrtX, uint32_t uiEndX, uint32_t uiY, TextAlignTPDF tTextAlign)
{
	uint16_t size;
	uint16_t buf;
	const lcd::LcdAttributeTPDF* pLcdAttri;

	if(!mLcd || !mFontModule)
	{
		return;
	}
	if(uiSatrtX >= mLcd->GetLcdAttribute()->Resolution.X ||
		 uiEndX >= mLcd->GetLcdAttribute()->Resolution.X ||
		 uiY >= mLcd->GetLcdAttribute()->Resolution.Y)
	{
		return;
	}
	if(uiSatrtX > uiEndX)
	{
		buf = uiSatrtX;
		uiSatrtX = uiEndX;
		uiEndX = buf;
	}
	switch(tTextAlign)
	{
		case drawing::TEXT_ALIGN_CENTER:
			pLcdAttri = mLcd->GetLcdAttribute();
			size = (uint16_t)MeasureString(String);
			buf = (uint16_t)pLcdAttri->Resolution.X;
			size > buf ? size = buf:NULL;
			DrawString(tPaint, String, (buf - size)>>1, uiY);
			break;
		case drawing::TEXT_ALIGN_RIGHT:
			size = MeasureString(String);
			if(size > (uiEndX - uiSatrtX))
			{
				DrawString(tPaint, String, uiSatrtX, uiY);
			}
			else
			{
				DrawString(tPaint, String, uiEndX - size, uiY);
			}
			break;
		default:
			DrawString(tPaint, String, uiSatrtX, uiY);
			break;
	}
}
///> lcd 画一张图
void drawing::DrawImage(image *tImg, uint32_t uiStartX, uint32_t uiStartY)
{
	uint32_t i, j;

	if(!mLcd || !mFontModule || !tImg)
	{
		return;
	}
	if((tImg->Width + uiStartX) > mLcd->GetLcdAttribute()->Resolution.X ||
					(tImg->Height + uiStartY) > mLcd->GetLcdAttribute()->Resolution.Y)
	{
		return;
	}
	if(tImg->ColorType == lcd::COLOR_TYPE_MONOCHROME)
	{
		if(tImg->DrawingDirect == image::DRAWINGDIRECT_VERTICAL)
		{
			j = tImg->Height >> 3;
			if(tImg->Height & 7)
			{
				j++;
			}
			for(i = 0; i < tImg->Width; i++)
			{
				mLcd->DrawVertical(uiStartX + i, uiStartY, tImg->GetStream() + i * j, tImg->Height);
			}
		}
		else
		{
			j = tImg->Width >> 3;
			if(tImg->Width & 7)
			{
				j++;
			}
			j++;
			for(i = 0; i < tImg->Height; i++)
			{
				mLcd->DrawHorizontal(uiStartX, uiStartY + i, tImg->GetStream() + i * j, tImg->Width);
			}
		}
	}
	else
	{
	}
}

const lcd::ResolutionTPDF* drawing::GetResolution(void)
{
	return(&mLcd->GetLcdAttribute()->Resolution);
}
